﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Zcu.PanelComposer.Views.UserControls
{
    /// <summary>
    /// Interaction logic for PanelCanvas.xaml
    /// </summary>
    public partial class PanelCanvas : UserControl
    {
        #region Fields

        private const string NameOfDoor = "BlockDoorA";
        private const int DefaultBlockLayer = 2;
        private Border _block = null;
        private bool _deleter = false;
        private bool _door = false;

        private bool _drawEllipse = false;
        private bool _drawRectangle = false;

        private Point _startingPoint;
        private Ellipse _ellipse = null;
        private Rectangle _rectangle = null;

        #endregion

        #region Dependency Property

        public int ForceRead
        {
            get { return (int)GetValue(ForceReadProperty); }
            set { SetValue(ForceReadProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ForceRead.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ForceReadProperty =
            DependencyProperty.Register("ForceRead", typeof(int), typeof(PanelCanvas), new UIPropertyMetadata(0));

        /// <summary>
        /// Gets or sets the poly collection.
        /// </summary>
        /// <value>
        /// The poly collection.
        /// </value>
        public PointCollection PolyCollection
        {
            get { return (PointCollection)GetValue(PolyCollectionProperty); }
            set { SetValue(PolyCollectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PolyCollection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PolyCollectionProperty =
            DependencyProperty.Register("PolyCollection", typeof(PointCollection), typeof(PanelCanvas), new UIPropertyMetadata(null));

        /// <summary>
        /// Gets or sets the serializable.
        /// </summary>
        /// <value>
        /// The serializable.
        /// </value>
        public string Serializable
        {
            get { return (string)GetValue(SerializableProperty); }
            set { SetValue(SerializableProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Serializable.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SerializableProperty =
            DependencyProperty.Register("Serializable", typeof(string), typeof(PanelCanvas), new UIPropertyMetadata(null));
       

        public int Height
        {
            get { return (int)GetValue(HeightProperty); }
            set { SetValue(HeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Height.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeightProperty =
            DependencyProperty.Register("Height", typeof(int), typeof(PanelCanvas), new UIPropertyMetadata(0));

        public int Floors
        {
            get { return (int)GetValue(FloorsProperty); }
            set { SetValue(FloorsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Floors.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FloorsProperty =
            DependencyProperty.Register("Floors", typeof(int), typeof(PanelCanvas), new UIPropertyMetadata(0));

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="PanelCanvas"/> class.
        /// </summary>
        public PanelCanvas()
        {
            InitializeComponent();

            Loaded += new RoutedEventHandler(PanelCanvas_Loaded);
        }

        #endregion

        #region Click methods

        private void Block_Click(object sender, RoutedEventArgs e)
        {
            _block = null;
            _deleter = false;
            _door = false;
            _drawRectangle = false;
            _drawEllipse = false;

            if (_rectangle != null && PolyCanvas.Children.Contains(_rectangle))
            {
                PolyCanvas.Children.Remove(_rectangle);
            }
            if (_ellipse != null && PolyCanvas.Children.Contains(_ellipse))
            {
                PolyCanvas.Children.Remove(_ellipse);
            }

            _ellipse = null;
            _rectangle = null;

            if (sender is RadioButton)
            {
                RadioButton btn = (RadioButton)sender;

                if (btn.Name == "Remove")
                {
                    _deleter = true;
                }
                else if (btn.Name == "BlockDoorA")
                {
                    _door = true;
                }
                else if (btn.Name == "Ellipse")
                {
                    _drawEllipse = true;

                    //_ellipse = new Ellipse();
                    //_ellipse.Fill = new SolidColorBrush(FillColor.SelectedColor);
                    //_ellipse.Stroke = new SolidColorBrush(BrushColor.SelectedColor);
                }
                else if (btn.Name == "Rectangle")
                {
                    _drawRectangle = true;
                }
                else
                {
                    Label lbl = new Label();
                    lbl.Template = (ControlTemplate)Application.Current.FindResource(btn.Name);

                    Viewbox vb = new Viewbox();
                    vb.Child = lbl;

                    _block = new Border();
                    _block.Tag = btn.Name;
                    _block.Height = Height;
                    _block.Child = vb;
                    _block.BorderBrush = Brushes.LightGray;
                    _block.BorderThickness = new Thickness(1);
                }
            }
        }

        #endregion

        #region Canvas methods

        /// <summary>
        /// Handles the MouseDown event of the PolyCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void PolyCanvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            var point = e.GetPosition(PolyCanvas);
            _startingPoint = point;

            FrameworkElement ele = null;
            if (_block != null && !IsCollide(_block, false, out ele))
            {
                for (int i = 0; i < Floors; i++)
                {
                    Viewbox vbOld = (Viewbox)_block.Child;
                    Label lOld = (Label)vbOld.Child;

                    Label lbl = new Label();
                    lbl.Template = lOld.Template;

                    Viewbox vb = new Viewbox();
                    vb.Child = lbl;

                    Border b = new Border();
                    b.Tag = _block.Tag;
                    b.Height = Height;
                    b.Child = vb;
                    b.BorderBrush = Brushes.LightGray;
                    b.BorderThickness = new Thickness(1);

                    Canvas.SetLeft(b, point.X - _block.ActualWidth / 2.0);
                    Canvas.SetTop(b, i * Height);
                    Canvas.SetZIndex(b, DefaultBlockLayer);
                    PolyCanvas.Children.Add(b);
                }
            }
            else if (_deleter)
            {
                List<FrameworkElement> delList = new List<FrameworkElement>();
                foreach (FrameworkElement child in PolyCanvas.Children)
                {
                    if (Canvas.GetZIndex(child) != DefaultBlockLayer || child is Line)
                        continue;

                    double pointer = point.X;

                    double lchild = Canvas.GetLeft(child);
                    double rchild = lchild + child.ActualWidth;

                    // negace vse je pred ci negace vse je za
                    if (!((pointer < lchild) || (pointer > rchild)))
                    {
                        delList.Add(child);
                    }
                }

                // ostraneni
                foreach (var item in delList)
                {
                    PolyCanvas.Children.Remove(item);
                }
            }
            else if (_door)
            {
                foreach (FrameworkElement child in PolyCanvas.Children)
                {
                    if (Canvas.GetZIndex(child) != DefaultBlockLayer || child is Line)
                        continue;

                    double pointer = point.X;
                    double lchild = Canvas.GetLeft(child);
                    double rchild = lchild + child.ActualWidth;
                    double tchild = Canvas.GetTop(child);

                    if (!((pointer < lchild) || (pointer > rchild)))
                    {
                        if (tchild >= (Height * (Floors - 1))
                            && child is Border && ((Border)child).Tag != null && (((Border)child).Tag.ToString().EndsWith("A")
                            || ((Border)child).Tag.ToString().EndsWith("A2")))
                        {
                            Label l = (Label)((Viewbox)((Border)child).Child).Child;
                            l.Template = (ControlTemplate)Application.Current.FindResource(NameOfDoor);
                            child.Tag = NameOfDoor;
                        }
                    }
                }
            }
            else if (_drawEllipse)
            {
                _ellipse = new Ellipse();
                _ellipse.Fill = new SolidColorBrush(FillColor.SelectedColor);
                _ellipse.Stroke = new SolidColorBrush(BrushColor.SelectedColor);

                Canvas.SetZIndex(_ellipse, 1);
                Canvas.SetTop(_ellipse, _startingPoint.Y);
                Canvas.SetLeft(_ellipse, _startingPoint.X);

                PolyCanvas.Children.Add(_ellipse);
            }
            else if (_drawRectangle)
            {
                _rectangle = new Rectangle();
                _rectangle.Fill = new SolidColorBrush(FillColor.SelectedColor);
                _rectangle.Stroke = new SolidColorBrush(BrushColor.SelectedColor);

                Canvas.SetZIndex(_rectangle, 1);
                Canvas.SetTop(_rectangle, _startingPoint.Y);
                Canvas.SetLeft(_rectangle, _startingPoint.X);

                PolyCanvas.Children.Add(_rectangle);
            }
        }

        /// <summary>
        /// Handles the MouseMove event of the PolyCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void PolyCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            var point = e.GetPosition(PolyCanvas);
            if (_block != null)
            {
                Canvas.SetLeft(_block, point.X - _block.ActualWidth/2.0);
                FrameworkElement ele = null;
                if (IsCollide(_block, false, out ele))
                    Cursor = Cursors.No;
                else
                    Cursor = Cursors.Arrow;
            }
            else if (_deleter)
            {
                Cursor = Cursors.Arrow;
                foreach (FrameworkElement child in PolyCanvas.Children)
                {
                    if (Canvas.GetZIndex(child) != DefaultBlockLayer || child is Line)
                        continue;

                    double pointer = point.X;

                    double lchild = Canvas.GetLeft(child);
                    double rchild = lchild + child.ActualWidth;

                    // negace vse je pred ci negace vse je za
                    if (!((pointer < lchild) || (pointer > rchild)))
                    {
                        Cursor = Cursors.Cross;
                        break;
                    }
                }
            }
            else if (_door)
            {
                Cursor = Cursors.Arrow;
                foreach (FrameworkElement child in PolyCanvas.Children)
                {
                    if (Canvas.GetZIndex(child) != DefaultBlockLayer || child is Line)
                        continue;

                    double pointer = point.X;
                    double lchild = Canvas.GetLeft(child);
                    double rchild = lchild + child.ActualWidth;
                    double tchild = Canvas.GetTop(child);

                    if (!((pointer < lchild) || (pointer > rchild)))
                    {
                        if (tchild >= (Height * (Floors - 1))
                            && child is Border && ((Border)child).Tag != null && (((Border)child).Tag.ToString().EndsWith("A")
                            || ((Border)child).Tag.ToString().EndsWith("A2")))
                        {
                            Cursor = Cursors.Hand;
                        }
                    }
                }
            }
            else if (_rectangle != null)
            {
                var x = Math.Min(point.X, _startingPoint.X);
                var y = Math.Min(point.Y, _startingPoint.Y);

                var w = Math.Max(point.X, _startingPoint.X) - x;
                var h = Math.Max(point.Y, _startingPoint.Y) - y;

                _rectangle.Width = w;
                _rectangle.Height = h;

                Canvas.SetLeft(_rectangle, x);
                Canvas.SetTop(_rectangle, y);
            }
            else if (_ellipse != null)
            {
                var x = Math.Min(point.X, _startingPoint.X);
                var y = Math.Min(point.Y, _startingPoint.Y);

                var w = Math.Max(point.X, _startingPoint.X) - x;
                var h = Math.Max(point.Y, _startingPoint.Y) - y;

                _ellipse.Width = w;
                _ellipse.Height = h;

                Canvas.SetLeft(_ellipse, x);
                Canvas.SetTop(_ellipse, y);
            }
        }

        /// <summary>
        /// Handles the MouseEnter event of the PolyCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void PolyCanvas_MouseEnter(object sender, MouseEventArgs e)
        {
            if (_block != null)
            {
                Canvas.SetZIndex(_block, DefaultBlockLayer);
                PolyCanvas.Children.Add(_block);
            }
        }

        /// <summary>
        /// Handles the MouseLeave event of the PolyCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void PolyCanvas_MouseLeave(object sender, MouseEventArgs e)
        {
            if (_block != null)
            {
                PolyCanvas.Children.Remove(_block);
            }

            Cursor = Cursors.Arrow;
        }

        /// <summary>
        /// Handles the MouseUp event of the PolyCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void PolyCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _rectangle = null;
            _ellipse = null;
        }

        #endregion

        #region Overrided methods

        /// <summary>
        /// Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.FrameworkElement"/> has been updated. The specific dependency property that changed is reported in the arguments parameter. Overrides <see cref="M:System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)"/>.
        /// </summary>
        /// <param name="e">The event data that describes the property that changed, as well as old and new values.</param>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == SerializableProperty)
            {
                SerializableChanged();
            }
            else if (e.Property == ForceReadProperty)
            {
                bool isContain = false;
                if (isContain = (_block != null && PolyCanvas.Children.Contains(_block)))
                {
                    PolyCanvas.Children.Remove(_block);
                }

                Serializable = SerializableCanvas(PolyCanvas);

                if (isContain)
                    PolyCanvas.Children.Add(_block);
            }

            base.OnPropertyChanged(e);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Handles the Loaded event of the PanelCanvas control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PanelCanvas_Loaded(object sender, RoutedEventArgs e)
        {
            SerializableChanged();
        }

        /// <summary>
        /// Serializables the changed.
        /// </summary>
        private void SerializableChanged()
        {
            PolyCanvas.Children.Clear();
            if (string.IsNullOrEmpty(Serializable))
            {
                int maxHeight = Height * Floors;
                if (maxHeight == 0) return;

                double circumference = 0;
                // pokud je kolekce nenulova, tak ji zobrazime
                if (PolyCollection != null)
                {
                    List<Line> lines = new List<Line>();
                    lines.Add(new Line() { X1 = 0.0, X2 = 0.0, Y1 = 0.0, Y2 = maxHeight, Stroke = Brushes.Black });
                    // spocitame obvod polygonu
                    for (int i = 0; i < PolyCollection.Count; i++)
                    {
                        Point first = PolyCollection[i];
                        // pokud jsme na konci, tak bereme v potaz prvni prvek
                        Point second = PolyCollection[i == (PolyCollection.Count - 1) ? 0 : i + 1];
                        // nascitavani delky sten
                        circumference += PointDistance(first, second);

                        lines.Add(new Line() { X1 = circumference, X2 = circumference, Y1 = 0.0, Y2 = maxHeight, Stroke = Brushes.Black });

                    }

                    if (Floors > 1)
                    {
                        for (int i = 1; i < Floors; i++)
                        {
                            lines.Add(new Line() { X1 = 0.0, X2 = circumference, Y1 = (Height * i), Y2 = (Height * i), Stroke = Brushes.Black });
                        }
                    }
                    PolyCanvas.Width = circumference;
                    PolyCanvas.Height = maxHeight;
                    // 

                    foreach (Line line in lines)
                    {
                        Canvas.SetZIndex(line, DefaultBlockLayer);
                        PolyCanvas.Children.Add(line);
                    }
                }

                //_isLocalSave = true;
                Serializable = SerializableCanvas(PolyCanvas);
            }
            else
            {
                Canvas temp = DeserializableCanvas(Serializable);
                PolyCanvas.Width = temp.Width;
                PolyCanvas.Height = temp.Height;
                List<UIElement> elements = new List<UIElement>();

                foreach (UIElement item in temp.Children)
                {
                    elements.Add(item);
                }

                temp.Children.Clear();

                foreach (UIElement item in elements)
                {
                    PolyCanvas.Children.Add(item);
                }
            }
            
        }

        private bool IsCollide(FrameworkElement ele, bool ignoreLine, out FrameworkElement collide)
        {
            collide = null;

            if (!PolyCanvas.Children.Contains(ele))
                return false;

            double left = Canvas.GetLeft(ele);
            double right = left + ele.ActualWidth;
            double z = Canvas.GetZIndex(ele);

            foreach (FrameworkElement child in PolyCanvas.Children)
            {
                if (child == ele || Canvas.GetZIndex(child) != z
                    || (ignoreLine && child is Line)
                    || (child is Line && ((Line)child).Y1 == ((Line)child).Y2))
                    continue;


                double lchild = child is Line ? ((Line)child).X1 : Canvas.GetLeft(child);
                double rchild = child is Line ? ((Line)child).X2 : lchild + child.ActualWidth;

                // negace vse je pred ci negace vse je za
                if (!((left < lchild && right < lchild) || (left > rchild && right > rchild)))
                {
                    collide = child;
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Deserializables the canvas.
        /// </summary>
        /// <param name="canvas">The canvas.</param>
        /// <returns></returns>
        private Canvas DeserializableCanvas(string canvas)
        {
            return (Canvas)System.Windows.Markup.XamlReader.Parse(canvas);
        }

        /// <summary>
        /// Serializables the canvas.
        /// </summary>
        /// <param name="canvas">The canvas.</param>
        /// <returns></returns>
        private string SerializableCanvas(Canvas canvas)
        {
            return System.Windows.Markup.XamlWriter.Save(canvas);
        }

        /// <summary>
        /// Points the distance.
        /// </summary>
        /// <param name="start">The start.</param>
        /// <param name="end">The end.</param>
        /// <returns></returns>
        private double PointDistance(Point start, Point end)
        {
            return Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
        }

        #endregion
    }
}
